#ifndef _MAP_HH_
#define _MAP_HH_
#include "CacheMap.hh"
#include <vector>
#include <ostream>
#include <new>


extern CacheMap torus;
extern FastSet<unsigned> visited;


/// Generic map
template<typename Cell_t>
struct Map
{
  Cell_t * _grid;

  /// Safe pointer
  Cell_t * grid(void) { ASSERT(_grid); return _grid; }

  /// Class builder
  Map() :
    _grid(NULL)
  { }


  typedef Cell_t* iterator;

  inline iterator begin()
  {
    ASSERT(_grid);
    return _grid;
  }

  inline iterator end()
  {
    return _grid+torus.size;
  }

    

  /// Class initializer
  void init(void)
  {
    TRACK();

    ASSERT(torus.size);
    try
      {
	_grid = new Cell_t[torus.size];
      }
    catch (std::bad_alloc& ba)
      {
	bug << "bad_alloc caught: " << ba.what() << std::endl;
      }
    ASSERT(_grid);
  }


  /// set to zero
  void clear(void)
  {
    ASSERT(_grid);
    ASSERT(torus.size);
    memset(_grid,0,torus.size*sizeof(Cell_t));
  }


  /// accessor with (row,col) coord
  inline Cell_t & at(int row, int col)
  {
    ASSERT(_grid);
    ASSERT(torus.to_uLocation(row,col)<torus.size);
    return _grid[torus.to_uLocation(row,col)];
  }

  inline const Cell_t at(int row, int col) const
  {
    ASSERT(_grid);
    ASSERT(torus.to_uLocation(row,col)<torus.size);
    return _grid[torus.to_uLocation(row,col)];
  }

  /// safe accessor with uLocation
  inline Cell_t & at(uLocation loc)
  {
    ASSERT(loc<torus.size);
    ASSERT(_grid);
    return _grid[(size_t)loc];
  }

  inline const Cell_t at(uLocation loc) const
  {
    ASSERT(loc<torus.size);
    ASSERT(_grid);
    return _grid[(size_t)loc];
  }


  /// accessor with uLocation
  inline Cell_t & operator[](uLocation loc)
  {
    return _grid[(size_t)loc];
  }

  inline const Cell_t operator[](uLocation loc) const
  {
    return _grid[(size_t)loc];
  }

  std::ostream & dump(std::ostream & os, bool sep = false) const
  {
    for(unsigned row=0; row<torus.rows; row++)
      {
	if(not sep) os << std::setw(3) << row << ": ";
	os << at(row,0);
	for(unsigned col=1; col<torus.cols; col++)
	  {
	    if(sep)
	      os << ' ' << at(row,col);
	    else
	      os << at(row,col);
	  }
	os << std::endl;
      }
    return os;
  }

  /// Save a gnuplot readable file to display potential
  /// splot 'file.dat' matrix with pm3d
  void dump_to_file(std::ostringstream & fname)
  {
#if DEBUG
    fname << ".dat";
    std::ofstream file(fname.str().c_str());

    if(file)
      {
	dump(file,true);
	file << std::endl;
	file.close();
      }
    else
      {
	bug << "could not open file " << fname << std::endl;
      }
#endif /* DEBUG */
  }



  /// class killer
  virtual ~Map()
  {
    TRACK();
    if (_grid != NULL) delete[] _grid;
  }

};


template<typename Cell_t>
std::ostream & operator<<(std::ostream & os, const Map<Cell_t> & map)
{
  return map.dump(os);
}







#endif /* _MAP_HH_ */
